/*
 * main.c
 *
 * Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

#include "main.h"

volatile uint16_t LED_delay = 1000; // led blinking delay in ms
// array of pointers to USB handlers, if active == USB_OTG_devX,
//    circular: USB_active[0] always points to first or only active buffer
#ifdef USE_USB
USB_OTG_CORE_HANDLE *USB_active[2] = {NULL, NULL};
#endif
#ifdef USBPA
USB_OTG_CORE_HANDLE USB_OTG_devA;
#endif
#ifdef USBPB
USB_OTG_CORE_HANDLE USB_OTG_devB;
#endif

volatile uint32_t LocalTime = 0;

uint8_t glob_ADC_gain = 0;

volatile uint32_t FLAG = 0;
#ifdef USE_ETH
static uint32_t  eth_on = 0;
#endif

#ifdef USEAD7794
static uint32_t ad7794_on = 0;
#endif

#ifdef USE_ETH
void ETH_LWIP_init(){
	if(EthInitStatus == 0){
		DBG("Error: can't initialize ethernet!\r\n");
	}else{
		if(!eth_on){
			if(LwIP_Init() && tcp_echoserver_init()) eth_on = 1;
		}
	}
}
#endif

#ifdef USEAD7794
#define ADC_direct()  setup_AD7794(EXTREFIN_1 | REF_DETECTION | UNIPOLAR_CODING, IEXC_DIRECT  | IEXC_1MA)
#define ADC_reverse() setup_AD7794(EXTREFIN_1 | REF_DETECTION | UNIPOLAR_CODING, IEXC_SWAPPED | IEXC_1MA)

void ADC_init(){
	uint8_t  i;
	ad7794_on = 0;
	i = reset_AD7794();
	if(i != ADC_NO_ERROR){
		if(i == ADC_ERR_NO_DEVICE){
			P("ADC signal is absent! Check connection.");
			newline();
		}else{ // try to reset next time
			FLAG |= FLAG_RESET_ADC;
		}
	}else{
		//if(!ADC_direct()
		if(!setup_AD7794(INTREFIN | REF_DETECTION | UNIPOLAR_CODING, IEXC_DIRECT  | IEXC_1MA)
			|| !AD7794_calibration(0)){
			DBG("Error: can't initialize AD7794!\r\n");
			FLAG |= FLAG_RESET_ADC; // try next time
		}else{
			ad7794_on = 1;
		}
	}
}
#endif // USEAD7794

void init_periph(){
	#ifdef USE_ETH
	ETH_LWIP_init();
	#endif
	#ifdef USEAD7794
	ADC_init();
	#endif // USEAD7794
}

#ifdef USEAD7794
/**
 * Reads ADC value
 * @return	readed count * 2 if FLAG_ADC_DOUBLECNV is not set
 * 			sum of direct & reverse Iref if FLAG_ADC_DOUBLECNV is set
 */
uint32_t readADC_value(){
	uint32_t val0, val1;
	uint8_t mul2 = 1;
	if(FLAG & FLAG_ADC_DOUBLECNV)
		if(!ADC_direct()) return 0;
	DBG("READ: A_direct = ");
	if( !(val0 = read_AD7794(0)) ){
		DBG(" err\r\n");
		return 0;
	}
	#ifdef EBUG
		printInt(STR(&val0), 4);
	#endif
	if(FLAG & FLAG_ADC_DOUBLECNV) do{ // double conversion
		#ifdef EBUG
			P(", A_reverse = ");
		#endif
		if(!ADC_reverse()) break;
		if( !(val1 = read_AD7794(0)) ) break;
		#ifdef EBUG
			printInt(STR(&val1), 4);
		#endif
		mul2 = 0;
		val0 += val1;
	}while(0);
	#ifdef EBUG
		newline();
	#endif
	if(mul2) val0 <<= 1; // multiply by two if there's no reverse value
	return val0;
}
#endif // USEAD7794

int main(){
	uint32_t nxt_LED_swch;
#ifdef USEAD7794
	uint32_t ADCvalReaded = 0;
#endif
	Set_System();
//	init_periph();
	nxt_LED_swch = LocalTime + LED_delay;
	while(1){
		if(nxt_LED_swch <= LocalTime){
			ToggleBit(LED_GPIOx, LED_PIN);
			nxt_LED_swch = LocalTime + LED_delay;
			//P("toggle!\r\n");
			//newline();
		}
		if(FLAG & FLAG_INIT_PERIPH){
			FLAG ^= FLAG_INIT_PERIPH;
			init_periph();
		}
		#ifdef USE_ETH
		if(FLAG & FLAG_ETH_RESET){
			FLAG ^= FLAG_ETH_RESET;
			ETH_SoftwareReset();
			ETH_BSP_Config();
			ETH_LWIP_init();
		}
		if(eth_on){
			if(ETH_CheckFrameReceived()){
				LwIP_Pkt_Handle();
			}
			LwIP_Periodic_Handle(LocalTime);
		}
		#endif // USE_ETH
		#ifdef USEAD7794
		if(FLAG & FLAG_CH_ADC_GAIN){
			FLAG ^= FLAG_CH_ADC_GAIN;
			if(ad7794_on){
				change_AD7794_gain(glob_ADC_gain);
				AD7794_calibration(0);
			}
		}
		if(FLAG & FLAG_READ_ADC){
			FLAG ^= FLAG_READ_ADC;
			if(ad7794_on){
				ADCvalReaded = readADC_value();
				if(!ADCvalReaded)
					P("OOOPS, error!\n\r");
				else
					printInt(STR(&ADCvalReaded), 4);
			}
		}
		if(FLAG & FLAG_RESET_ADC){
			FLAG ^= FLAG_RESET_ADC;
			ADC_init();
		}
		if(FLAG & FLAG_ADC_IEXC_DIR){ // direct Iexc
			FLAG ^= FLAG_ADC_IEXC_DIR;
			if(ad7794_on){
				if(!ADC_direct()) ad7794_on = 0;
			}
		}
		if(FLAG & FLAG_ADC_IEXC_REV){ // reverce Iexc
			FLAG ^= FLAG_ADC_IEXC_REV;
			if(ad7794_on){
				if(!ADC_reverse()) ad7794_on = 0;
			}
		}
		#endif // USEAD7794
	}
}

inline void Set_System(){
	// SysTick setup: 1 ms
	SysTick_Config(SystemCoreClock / 1000);
	// Ports configuration
	Ports_Config();
#ifdef USBPA
	USBD_Init(&USB_OTG_devA,USB_OTG_FS_CORE_ID,&USR_desc,&USBD_CDC_cb,&USR_cb);
#endif
#ifdef USBPA
	USBD_Init(&USB_OTG_devB,USB_OTG_HS_CORE_ID,&USR_desc,&USBD_CDC_cb,&USR_cb);
#endif
#ifdef USE_ETH
	ETH_BSP_Config();
#endif
#ifdef USEAD7794
	SPI_Config();
#endif // USEAD7794
}

/*
 * Send char array wrd thru USB
 */
void prnt(uint8_t *wrd){
#ifdef USE_USB
	if(!wrd || !outp_active[0]) return;
	uint8_t *p = wrd;
	uint32_t L = 0;
	while(*p++) L++;
	VCP_DataTx(USB_active[0], wrd, L);
#endif // USE_USB
}

size_t strlen(const char *s){
	size_t L = 0;
	while(*s++) L++;
	return L;
}

/**
 * Send to terminal an integer value
 * @param val - value itself
 * @param len - bytelength of val
 */
void printInt(_U_ volatile uint8_t *val, _U_ int8_t len){
	#ifdef USE_USB
	if(!outp_active[0]) return;
	uint32_t _U_ Number = 0;
	int8_t i = len, ch;
	uint8_t decimal_buff[11];
	_U_ uint8_t *ptr;
	P("0x");
	void putch(uint8_t c){
		if(c < 10)
			USB_Send_Data(USB_active[0], c + '0');
		else
			USB_Send_Data(USB_active[0], c + 'a' - 10);
	}
	while(--i >= 0){
		putch(val[i] >> 4);
		putch(val[i] & 0x0f);
	}
	if(len < 5){ // print in decimal only uint32_t and lesser
		P(" (");
		for(i = 0; i < 11; i++)
			decimal_buff[i] = 0;
		ch = 9;
		Number = *((uint32_t*) val);/*
		ptr = (uint8_t*)&Number;
		for(i = 0; i < len; i++) // copy buffer with uint
			*ptr++ = *val++;*/
		do{
			i = Number % 10;
			decimal_buff[ch--] = i + '0';
			Number /= 10;
		}while(Number && ch > -1);
		P(&decimal_buff[ch+1]);
		USB_Send_Data(USB_active[0], ')');
	}
	newline();
	#endif
}
